import React, { useState, useEffect } from 'react';
import './App.css';

// Game constants
const DIFFICULTY_LEVELS = {
  easy: { rows: 9, cols: 9, mines: 10 },
  medium: { rows: 16, cols: 16, mines: 40 },
  hard: { rows: 16, cols: 30, mines: 99 }
};

const CUSTOM_DIFFICULTY = 'custom';

// Helper functions
const createEmptyBoard = (rows, cols) => {
  return Array(rows).fill().map(() => 
    Array(cols).fill().map(() => ({
      isMine: false,
      isVisible: false,
      isFlagged: false,
      adjacentMines: 0
    }))
  );
};

const placeMines = (board, rows, cols, mines, startX, startY) => {
  const newBoard = board.map(row => [...row]);
  let minesPlaced = 0;
  
  while (minesPlaced < mines) {
    const x = Math.floor(Math.random() * rows);
    const y = Math.floor(Math.random() * cols);
    
    // Don't place mine on first click or where mine already exists
    if ((x === startX && y === startY) || newBoard[x][y].isMine) {
      continue;
    }
    
    newBoard[x][y].isMine = true;
    minesPlaced++;
  }
  
  return newBoard;
};

const calculateAdjacentMines = (board, rows, cols) => {
  const newBoard = board.map(row => [...row]);
  
  for (let x = 0; x < rows; x++) {
    for (let y = 0; y < cols; y++) {
      if (!newBoard[x][y].isMine) {
        let count = 0;
        
        // Check all 8 neighbors
        for (let dx = -1; dx <= 1; dx++) {
          for (let dy = -1; dy <= 1; dy++) {
            if (dx === 0 && dy === 0) continue;
            
            const nx = x + dx;
            const ny = y + dy;
            
            if (nx >= 0 && nx < rows && ny >= 0 && ny < cols && newBoard[nx][ny].isMine) {
              count++;
            }
          }
        }
        
        newBoard[x][y].adjacentMines = count;
      }
    }
  }
  
  return newBoard;
};

const revealCell = (board, rows, cols, x, y) => {
  // If cell is already visible or flagged, do nothing
  if (board[x][y].isVisible || board[x][y].isFlagged) {
    return board;
  }
  
  const newBoard = board.map(row => [...row]);
  const queue = [{x, y}];
  // Keep track of visited cells to avoid infinite loops
  const visited = new Set();
  visited.add(`${x},${y}`);
  
  // Use BFS instead of recursion to avoid stack overflow
  while (queue.length > 0) {
    const {x, y} = queue.shift();
    
    // Skip if already visible or flagged
    if (newBoard[x][y].isVisible || newBoard[x][y].isFlagged) {
      continue;
    }
    
    newBoard[x][y].isVisible = true;
    
    // If cell has no adjacent mines, add neighbors to queue
    if (newBoard[x][y].adjacentMines === 0 && !newBoard[x][y].isMine) {
      for (let dx = -1; dx <= 1; dx++) {
        for (let dy = -1; dy <= 1; dy++) {
          // Skip the cell itself
          if (dx === 0 && dy === 0) continue;
          
          const nx = x + dx;
          const ny = y + dy;
          
          // Check bounds
          if (nx >= 0 && nx < rows && ny >= 0 && ny < cols) {
            const key = `${nx},${ny}`;
            // Only add to queue if not visited yet
            if (!visited.has(key)) {
              queue.push({x: nx, y: ny});
              visited.add(key);
            }
          }
        }
      }
    }
  }
  
  return newBoard;
};

const toggleFlag = (board, x, y) => {
  if (board[x][y].isVisible) return board;
  
  const newBoard = board.map(row => [...row]);
  newBoard[x][y].isFlagged = !newBoard[x][y].isFlagged;
  return newBoard;
};

const checkWin = (board, rows, cols, mines) => {
  let revealedCells = 0;
  
  for (let x = 0; x < rows; x++) {
    for (let y = 0; y < cols; y++) {
      if (board[x][y].isVisible) {
        revealedCells++;
      }
    }
  }
  
  // Win if all non-mine cells are revealed
  return revealedCells === (rows * cols - mines);
};

function App() {
  const [difficulty, setDifficulty] = useState('easy');
  const [board, setBoard] = useState([]);
  const [gameStatus, setGameStatus] = useState('idle'); // idle, playing, won, lost
  const [flags, setFlags] = useState(0);
  const [time, setTime] = useState(0);
  const [timerId, setTimerId] = useState(null);
  const [mouseDown, setMouseDown] = useState({ left: false, right: false });
  const [quickRevealCells, setQuickRevealCells] = useState([]); // Track cells to highlight for quick reveal
  const [customSettings, setCustomSettings] = useState({
    rows: 9,
    cols: 9,
    mines: 10
  });
  
  // Game settings based on difficulty
  const gameSettings = difficulty === CUSTOM_DIFFICULTY 
    ? customSettings
    : DIFFICULTY_LEVELS[difficulty];
  
  const { rows, cols, mines } = gameSettings;
  
  // Validate custom settings
  const validateCustomSettings = (settings) => {
    const { rows, cols, mines } = settings;
    const maxMines = rows * cols - 1;
    return {
      rows: Math.max(5, Math.min(20, rows)), // Limit grid size between 5x5 and 20x20
      cols: Math.max(5, Math.min(30, cols)),
      mines: Math.max(1, Math.min(maxMines, mines))
    };
  };
  
  // Update custom settings
  const updateCustomSettings = (newSettings) => {
    const validated = validateCustomSettings({ ...customSettings, ...newSettings });
    setCustomSettings(validated);
  };
  
  // Initialize game
  const initGame = () => {
    const emptyBoard = createEmptyBoard(rows, cols);
    setBoard(emptyBoard);
    setGameStatus('idle');
    setFlags(0);
    setTime(0);
    if (timerId) {
      clearInterval(timerId);
      setTimerId(null);
    }
  };
  
  // Start game (first click)
  const startGame = (startX, startY) => {
    if (gameStatus === 'playing') return;
    
    let newBoard = createEmptyBoard(rows, cols);
    newBoard = placeMines(newBoard, rows, cols, mines, startX, startY);
    newBoard = calculateAdjacentMines(newBoard, rows, cols);
    newBoard = revealCell(newBoard, rows, cols, startX, startY);
    
    setBoard(newBoard);
    setGameStatus('playing');
    setFlags(0);
    setTime(0);
    
    // Clear any existing timer
    if (timerId) {
      clearInterval(timerId);
    }
    
    // Start timer
    const id = setInterval(() => {
      setTime(prev => prev + 1);
    }, 1000);
    setTimerId(id);
  };
  
  // Handle cell click
  const handleCellClick = (x, y) => {
    // First click initialization
    if (gameStatus === 'idle') {
      startGame(x, y);
      return;
    }
    
    // Ignore if game is not playing or cell is flagged
    if (gameStatus !== 'playing' || board[x][y].isFlagged) return;
    
    // If mine is clicked, game over
    if (board[x][y].isMine) {
      // Reveal all mines
      const newBoard = board.map(row => 
        row.map(cell => 
          cell.isMine ? {...cell, isVisible: true} : cell
        )
      );
      setBoard(newBoard);
      setGameStatus('lost');
      setFlags(0); // Reset flag count when game is lost
      if (timerId) {
        clearInterval(timerId);
        setTimerId(null);
      }
      return;
    }
    
    // Reveal cell(s)
    const newBoard = revealCell(board, rows, cols, x, y);
    setBoard(newBoard);
    
    // Check win condition
    if (checkWin(newBoard, rows, cols, mines)) {
        setGameStatus('won');
        setFlags(0); // Reset flag count when game is won
        if (timerId) {
          clearInterval(timerId);
          setTimerId(null);
        }
      }
  };
  
  // Handle right click (flag)
  const handleRightClick = (e, x, y) => {
    e.preventDefault();
    
    if (gameStatus !== 'playing') return;
    
    // Don't allow flagging visible cells
    if (board[x][y].isVisible) return;
    
    const newBoard = toggleFlag(board, x, y);
    setBoard(newBoard);
    
    // Update flag count
    // If cell is now flagged, we added a flag (diff = 1)
    // If cell is now unflagged, we removed a flag (diff = -1)
    const flagDiff = newBoard[x][y].isFlagged ? 1 : -1;
    setFlags(prev => prev + flagDiff);
  };
  
  // Handle mouse down events for double click feature
  const handleMouseDown = (e, x, y) => {
    if (gameStatus !== 'playing') return;
    
    if (e.button === 0) { // Left mouse button
      setMouseDown(prev => ({ ...prev, left: true }));
    } else if (e.button === 2) { // Right mouse button
      setMouseDown(prev => ({ ...prev, right: true }));
    }
    
    // If both buttons are pressed and cell is visible with adjacent mines
    if ((mouseDown.left && e.button === 2) || (mouseDown.right && e.button === 0)) {
      if (board[x][y].isVisible && board[x][y].adjacentMines > 0) {
        // Calculate neighbors for highlight
        const neighbors = [];
        for (let dx = -1; dx <= 1; dx++) {
          for (let dy = -1; dy <= 1; dy++) {
            if (dx === 0 && dy === 0) continue;
            
            const nx = x + dx;
            const ny = y + dy;
            
            if (nx >= 0 && nx < rows && ny >= 0 && ny < cols) {
              neighbors.push(`${nx}-${ny}`);
            }
          }
        }
        setQuickRevealCells(neighbors);
      }
    }
  };
  
  // Handle mouse up events
  const handleMouseUp = (e, x, y) => {
    // Clear quick reveal highlight first
    setQuickRevealCells([]);
    
    if (e.button === 0) { // Left mouse button
      setMouseDown(prev => ({ ...prev, left: false }));
    } else if (e.button === 2) { // Right mouse button
      setMouseDown(prev => ({ ...prev, right: false }));
    }
    
    // If both buttons were pressed, perform quick reveal
    if (mouseDown.left && mouseDown.right) {
      if (board[x][y].isVisible && board[x][y].adjacentMines > 0) {
        quickReveal(x, y);
      }
    }
  };
  
  // Handle mouse leave events
  const handleMouseLeave = () => {
    setMouseDown({ left: false, right: false });
    // Clear quick reveal highlight
    setQuickRevealCells([]);
  };
  
  // Quick reveal functionality (double click)
  const quickReveal = (x, y) => {
    if (!board[x][y].isVisible || board[x][y].isFlagged) return;
    
    // Count flagged neighbors
    let flaggedNeighbors = 0;
    const neighbors = [];
    
    for (let dx = -1; dx <= 1; dx++) {
      for (let dy = -1; dy <= 1; dy++) {
        if (dx === 0 && dy === 0) continue;
        
        const nx = x + dx;
        const ny = y + dy;
        
        if (nx >= 0 && nx < rows && ny >= 0 && ny < cols) {
          neighbors.push({ x: nx, y: ny });
          if (board[nx][ny].isFlagged) {
            flaggedNeighbors++;
          }
        }
      }
    }
    
    // If flagged neighbors equal adjacent mines, reveal all unflagged neighbors
    if (flaggedNeighbors === board[x][y].adjacentMines) {
      let newBoard = [...board];
      let gameOver = false;
      
      for (const neighbor of neighbors) {
        const { x: nx, y: ny } = neighbor;
        
        // Skip flagged cells
        if (newBoard[nx][ny].isFlagged) continue;
        
        // If neighbor is a mine, game over
        if (newBoard[nx][ny].isMine) {
          gameOver = true;
          newBoard = newBoard.map(row => 
            row.map(cell => 
              cell.isMine ? {...cell, isVisible: true} : cell
            )
          );
          break;
        }
        
        // Reveal the cell
        newBoard = revealCell(newBoard, rows, cols, nx, ny);
      }
      
      setBoard(newBoard);
      
      if (gameOver) {
        setGameStatus('lost');
        setFlags(0); // Reset flag count when game is lost
        if (timerId) {
          clearInterval(timerId);
          setTimerId(null);
        }
      } else if (checkWin(newBoard, rows, cols, mines)) {
        setGameStatus('won');
        setFlags(0); // Reset flag count when game is won
        if (timerId) {
          clearInterval(timerId);
          setTimerId(null);
        }
      }
    }
  };
  
  // Change difficulty
  const changeDifficulty = (level) => {
    setDifficulty(level);
    // When switching to custom, initialize with current settings
    if (level === CUSTOM_DIFFICULTY) {
      const current = DIFFICULTY_LEVELS[difficulty] || DIFFICULTY_LEVELS.easy;
      setCustomSettings({
        rows: current.rows,
        cols: current.cols,
        mines: current.mines
      });
    }
  };
  
  // Initialize on mount and when difficulty or customSettings changes
  useEffect(() => {
    initGame();
  }, [difficulty, customSettings]);
  
  // Validate custom settings when they change
  useEffect(() => {
    if (difficulty === CUSTOM_DIFFICULTY) {
      const validated = validateCustomSettings(customSettings);
      if (validated.rows !== customSettings.rows || 
          validated.cols !== customSettings.cols || 
          validated.mines !== customSettings.mines) {
        setCustomSettings(validated);
      }
    }
  }, [customSettings, difficulty]);
  
  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (timerId) {
        clearInterval(timerId);
      }
    };
  }, []);
  
  return (
    <div className="minesweeper">
      <h1>Minesweeper</h1>
      
      <div className="game-container">
        <div className="game-info">
          <div className="mines-counter">Mines: {mines - flags}</div>
          <button 
            className="reset-button" 
            onClick={initGame}
          >
            {gameStatus === 'lost' ? '🤯' : gameStatus === 'won' ? '😎' : '🙂'}
          </button>
          <div className="timer">Time: {time}</div>
        </div>
        
        <div className="difficulty-selector">
          <button 
            className={difficulty === 'easy' ? 'active' : ''} 
            onClick={() => changeDifficulty('easy')}
          >
            Easy (9×9, 10 mines)
          </button>
          <button 
            className={difficulty === 'medium' ? 'active' : ''} 
            onClick={() => changeDifficulty('medium')}
          >
            Medium (16×16, 40 mines)
          </button>
          <button 
            className={difficulty === 'hard' ? 'active' : ''} 
            onClick={() => changeDifficulty('hard')}
          >
            Hard (16×30, 99 mines)
          </button>
          <button 
            className={difficulty === CUSTOM_DIFFICULTY ? 'active' : ''} 
            onClick={() => changeDifficulty(CUSTOM_DIFFICULTY)}
          >
            Custom
          </button>
          {difficulty === CUSTOM_DIFFICULTY && (
            <div className="custom-settings">
              <div className="setting-group">
                <label>
                  Rows:
                  <input 
                    type="number" 
                    min="5" 
                    max="20"
                    value={customSettings.rows} 
                    onChange={(e) => updateCustomSettings({ rows: parseInt(e.target.value) || 9 })}
                    className="custom-input"
                  />
                </label>
              </div>
              <div className="setting-group">
                <label>
                  Columns:
                  <input 
                    type="number" 
                    min="5" 
                    max="30"
                    value={customSettings.cols} 
                    onChange={(e) => updateCustomSettings({ cols: parseInt(e.target.value) || 9 })}
                    className="custom-input"
                  />
                </label>
              </div>
              <div className="setting-group">
                <label>
                  Mines:
                  <input 
                    type="number" 
                    min="1" 
                    max={customSettings.rows * customSettings.cols - 1}
                    value={customSettings.mines} 
                    onChange={(e) => updateCustomSettings({ mines: parseInt(e.target.value) || 10 })}
                    className="custom-input"
                  />
                  <span className="mine-range"> (1-{customSettings.rows * customSettings.cols - 1})</span>
                </label>
              </div>
            </div>
          )}
        </div>
        
        <div 
          className="board" 
          style={{ 
            gridTemplateColumns: `repeat(${cols}, 30px)`,
            maxWidth: '100%',
            overflowX: 'auto'
          }}
          onMouseLeave={handleMouseLeave}
        >
          {board.map((row, x) => 
            row.map((cell, y) => (
              <div
                key={`${x}-${y}`}
                className={`cell ${
                  cell.isVisible ? 'visible' : ''
                } ${
                  cell.isFlagged ? 'flagged' : ''
                } ${
                  quickRevealCells.includes(`${x}-${y}`) ? 'highlight' : ''
                }`}
                onClick={() => handleCellClick(x, y)}
                onContextMenu={(e) => handleRightClick(e, x, y)}
                onMouseDown={(e) => handleMouseDown(e, x, y)}
                onMouseUp={(e) => handleMouseUp(e, x, y)}
              >
                {!cell.isVisible && cell.isFlagged && '🚩'}
                {cell.isVisible && cell.isMine && '💣'}
                {cell.isVisible && !cell.isMine && cell.adjacentMines > 0 && (
                  <span className={`adjacent-${cell.adjacentMines}`}>
                    {cell.adjacentMines}
                  </span>
                )}
                {cell.isVisible && !cell.isMine && cell.adjacentMines === 0 && ''}
              </div>
            ))
          )}
        </div>
        
        <div className="message">
          {gameStatus === 'lost' && 'Game Over! 💥'}
          {gameStatus === 'won' && 'You Win! 🎉'}
        </div>
      </div>
    </div>
  );
}

export default App;